#include "EventLoop.h"

thread_local EventLoop *t_loopInThisThread = nullptr;

int kPollTimeMs = 10000;

int createEventfd()
{
    int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    if (evtfd < 0)
    {
        std::cout << "createEventfd error" << std::endl;
        abort();
    }
    return evtfd;
}

EventLoop *getEventLoopOfCurrentThread()
{
    return t_loopInThisThread;
}

EventLoop::EventLoop()
    : looping_(false),
      quit_(false),
      eventHandling_(false),
      callingPendingFunctors_(false),
      epoller_(new Epoller(this)),
      threadId_(CurrentThread::tid()),
      currentActiveChannel_(nullptr),
      wakeupFd_(createEventfd())
{
    wakeupChannel_ = std::unique_ptr<Channel>(new Channel(this, wakeupFd_));
    std::cout << "EventLoop created " << this << " in thread " << threadId_ << std::endl;
    if (t_loopInThisThread != nullptr)
    {
        std::cout << "Another EventLoop " << t_loopInThisThread
                  << " exists in this thread " << std::endl;
    }
    else
    {
        t_loopInThisThread = this;
    }
    wakeupChannel_->setReadCallback(std::bind(&EventLoop::handleRead, this));
    wakeupChannel_->enableRead();
}

EventLoop::~EventLoop()
{
    std::cout << "EventLoop " << this << " of thread " << threadId_
              << " destructs in thread " << CurrentThread::tid() << std::endl;
    wakeupChannel_->disableAll();
    wakeupChannel_->remove();
    ::close(wakeupFd_);
    t_loopInThisThread = nullptr;
}

void EventLoop::loop()
{
    assert(!looping_);
    assertInLoopThread();
    looping_ = true;
    quit_ = false;
    std::cout << "EventLoop " << this << " start looping";
    while (!quit_)
    {
        activeChannels_.clear();
        int activeNum = epoller_->poll(-1, activeChannels_);
        eventHandling_ = true;
        for (int i = 0; i < activeNum; ++i)
        {
            currentActiveChannel_ = activeChannels_[i];
            currentActiveChannel_->handleEvent();
        }
        currentActiveChannel_ = nullptr;
        eventHandling_ = false;
        doPendingFunctor();
    }
    std::cout << "EventLoop " << this << " stop looping" << std::endl;
    looping_ = false;
}

void EventLoop::quit()
{
    quit_ = true;
    if (!isInLoopThread())
    {
        wakeup();
    }
}

void EventLoop::updateChannel(Channel *channel)
{
    assert(channel->ownerLoop() == this);
    assertInLoopThread();
    epoller_->updateChannel(channel);
}

void EventLoop::removeChannel(Channel *channel)
{
    assert(channel->ownerLoop() == this);
    assertInLoopThread();
    if (eventHandling_)
    {
        assert(currentActiveChannel_ == channel ||
               std::find(std::begin(activeChannels_), std::end(activeChannels_), channel) == activeChannels_.end());
    }
    epoller_->removeChannel(channel);
}

bool EventLoop::hasChannel(Channel *channel)
{
    assert(channel->ownerLoop() == this);
    assertInLoopThread();
    return epoller_->hasChannel(channel);
}

bool EventLoop::isInLoopThread() const
{
    return threadId_ == CurrentThread::tid();
}

void EventLoop::assertInLoopThread()
{
    if (!isInLoopThread())
    {
        aborttNotInLoopThread();
    }
}

void EventLoop::aborttNotInLoopThread()
{
    std::cout << "Not in loop thread, eventloop theadId: " << threadId_
              << " current thread id: " << CurrentThread::tid() << std::endl;
}

void EventLoop::doPendingFunctor()
{
    decltype(functors_) functors;
    callingPendingFunctors_ = true;
    {
        std::lock_guard<std::mutex> lock(mutex_);
        functors.swap(functors_);
    }
    for (const auto &functor : functors)
    {
        functor();
    }
    callingPendingFunctors_ = false;
}

void EventLoop::wakeup()
{
    uint64_t one = 1;
    ssize_t n = ::write(wakeupFd_, &one, sizeof one);
    if (n != sizeof one)
    {
        std::cout << "EventLoop::wakeup writes " << n << " bytes, instead of 8" << std::endl;
    }
}

void EventLoop::handleRead()
{
    int64_t one;
    ssize_t n = ::read(wakeupFd_, &one, sizeof one);
    if (n != sizeof one)
    {
        std::cout << "EventLoop::handleRead() reads " << n << " bytes instead of 8" << std::endl;
    }
}

size_t EventLoop::queueSize()
{
    std::lock_guard<std::mutex> lock(mutex_);
    return functors_.size();
}

void EventLoop::queueInLoop(std::function<void()> cb)
{
    {
        std::lock_guard<std::mutex> lock(mutex_);
        functors_.push_back(cb);
    }
    if (!isInLoopThread() || callingPendingFunctors_)
    {
        wakeup();
    }
}

void EventLoop::runInLoop(std::function<void()> cb)
{
    /*
    note: 如何在其他线程中唤醒当前线程执行任务???
    参考该函数的实现:
    1.定义一个函数接口runInLoop,判断当前线程是否是执行任务的线程(可以通过线程id判断)
        如果是,则直接执行任务
        如果不是,则将任务添加到任务队列中,并唤醒执行任务的线程
    
    2.唤醒的实现: 
        这里使用的是wakeup,wakeupChannel在EventLoop构造时被注册到Epoller_中,
        当当前函数调用wakeup时,会向wakeupChannel中写入数据,从而唤醒执行任务的线程的Epoller_中的事件循环,
        事件循环在处理wakeupChannel的读事件后(象征性的读一下),会调用doPendingFunctor,
        执行任务队列中的任务,从而达到唤醒执行任务的线程的效果
    */
    if (isInLoopThread())
    {
        cb();
    }
    else
    {
        queueInLoop(cb);
    }
}
